#ifndef END_CONDITION_HPP
#define END_CONDITION_HPP

#include "error.hpp"
#include "objective_function.hpp"
#include <ctime>
#include <vector>

class end_condition_i
{
protected:
        end_condition_i ( void )
        {
        }

public:
        virtual ~end_condition_i ( void )
        {
        }

        virtual void reset      ( void ) = 0;
        virtual bool examine    ( const objective_function_i & objective_function ) const = 0;
};

class timeout_end_condition_c : public end_condition_i
{
        long max_time_seconds;
        long time_started_seconds;

public:
        virtual ~timeout_end_condition_c ( void )
        {
        }

        timeout_end_condition_c ( long max_time_seconds ) :
                max_time_seconds        ( max_time_seconds ),
                time_started_seconds    ( time ( NULL ) )
        {
        }

        virtual void reset ( void )
        {
                time_started_seconds = time ( NULL );
        }

        virtual bool examine ( const objective_function_i & objective_function ) const
        {
                return time ( NULL ) - time_started_seconds >= max_time_seconds;
        }
};

class minimum_threshold_end_condition_c : public end_condition_i
{
        double threshold;

public:
        virtual ~minimum_threshold_end_condition_c ( void )
        {
        }

        minimum_threshold_end_condition_c ( double threshold ) :
                threshold ( threshold )
        {
        }

        virtual void reset ( void )
        {
        }

        virtual bool examine ( const objective_function_i & objective_function ) const
        {
                return objective_function.evaluate () <= threshold ;
        }
};

class complex_end_condition_c : public end_condition_i
{
public:
        typedef enum
        {
                LOGICAL_OPERATOR_AND,
                LOGICAL_OPERATOR_OR,
        } logical_operator_e;

private:
        typedef std::vector < end_condition_i * > end_conditions_v;

        enum
        {
                TIMEOUT                 = 0,
                MINIMUM_THRESHOLD       = 1,

                MAXIMUM_CONDITIONS_COUNT,
        };
        
        logical_operator_e      logical_operator;
        end_conditions_v *      end_conditions;

public:    
        virtual ~complex_end_condition_c ( void )
        {
                if ( NULL != end_conditions )
                {
                        end_conditions_v::iterator i;
                        for ( i = end_conditions->begin (); i != end_conditions->end (); i ++ )
                        {
                                end_condition_i * condition = ( end_condition_i * ) * i;
                                if ( NULL != condition )
                                {
                                        delete condition;
                                        condition = NULL;
                                }
                        }

                        delete end_conditions;
                        end_conditions = NULL;
                }
        }

        complex_end_condition_c ( logical_operator_e logical_operator ) :
                logical_operator ( logical_operator ),
                end_conditions ( NULL )
        {
                end_conditions = new end_conditions_v ( MAXIMUM_CONDITIONS_COUNT, NULL );
        }

        void set_timeout ( long max_time_seconds )
        {
                throw_if ( NULL != end_conditions->at ( TIMEOUT ), ERROR_INVALID_END_CONDITION );
                end_conditions->at ( TIMEOUT ) = new timeout_end_condition_c ( max_time_seconds );
        }

        void set_minimum_threshold ( double threshold )
        {
                throw_if ( NULL != end_conditions->at ( MINIMUM_THRESHOLD ), ERROR_INVALID_END_CONDITION );
                end_conditions->at ( MINIMUM_THRESHOLD ) = new minimum_threshold_end_condition_c ( threshold );
        }

        virtual void reset ( void )
        {
                end_conditions_v::iterator i;
                for ( i = end_conditions->begin (); i != end_conditions->end (); i ++ )
                {
                        end_condition_i * condition = ( ( end_condition_i * ) * i );
                        condition->reset ();
                }
        }

        bool examine ( const objective_function_i & objective_function ) const
        {
                bool return_value;

                switch ( logical_operator )
                {
                        case LOGICAL_OPERATOR_AND:
                                return_value = true;
                                break;
                        case LOGICAL_OPERATOR_OR:
                                return_value = false;
                                break;
                        default:
                                throw ERROR_STATE_UNEXPECTED;
                }

                end_conditions_v::const_iterator i;
                for ( i = end_conditions->begin (); i != end_conditions->end (); i ++ )
                {
                        const end_condition_i * condition = ( ( const end_condition_i * ) * i );
                        bool current_value = condition->examine ( objective_function );

                        switch ( logical_operator )
                        {
                                case LOGICAL_OPERATOR_AND:
                                        return_value = return_value && current_value;
                                        break;
                                case LOGICAL_OPERATOR_OR:
                                        return_value = return_value || current_value;
                                        break;
                                default:
                                        throw ERROR_STATE_UNEXPECTED;
                        }
                }

                return return_value;
        }
};

#endif

